package com.glodon.paas.databag.biz.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import jsonij.json.JPath;
import jsonij.parser.ParserException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import com.glodon.paas.databag.biz.RevitConstructionBiz;
import com.glodon.paas.databag.util.WebService;
import com.glodon.paas.exception.PaasServerExceptionFactory;

@Component
public class RevitConstructionBizImpl implements RevitConstructionBiz {
    private Logger logger = LoggerFactory.getLogger(RevitConstructionBizImpl.class);
    @Value("${databag.revit.construction.ttl:24}")
    private int revitConstructionTtl;
    @Value("${databag.revit.parse.timeout:5}")
    private int revitParseTimeout;
    @Value("${databag.revit.parse.thread:10}")
    private int revitParseThread;
    private ExecutorService executor;

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private WebService storage;

    @Override
    public void parseRevitConstruction(final String databagId, final String path, final String manifest) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                String parsed = redisTemplate.opsForValue().get(databagId);
                if (parsed != null) {// redis中已经存在缓存的构建属性信息或正在初始化redis
                    return;
                }
                InputStream is = null;
                BufferedReader reader = null;
                try {
                    logger.info("parsing revit construction for databag id:{}", databagId);
                    jsonij.json.Value propValue = JPath.evaluate(manifest, "/Metadata/Properties");
                    if (propValue == null) {
                        return;
                    }
                    String propPath = propValue.getString();
                    is = storage.getResource(path + "/" + propPath);
                    // 标示解析阶段开始，防止并发解析
                    Boolean parsing = redisTemplate.opsForValue().setIfAbsent(databagId, "parsing");
                    if (parsing == false) {// 其他线程或节点正在解析，则退出
                        return;
                    }
                    // 设置解析超时时间，防止解析过程被意外中断，导致后续不能重试解析
                    redisTemplate.expire(databagId, revitParseTimeout, TimeUnit.MINUTES);
                    reader = new BufferedReader(new InputStreamReader(is));
                    long begin = System.currentTimeMillis();
                    String line = null;
                    int lineCount = 0;
                    while ((line = reader.readLine()) != null) {
                        try {
                            String id = parseConstructionId(databagId, line);
                            redisTemplate.opsForValue().set(databagId + ":" + id, line, revitConstructionTtl,
                                    TimeUnit.HOURS);
                            lineCount++;
                        } catch (Exception e) {
                            logger.error("invalid revit construction format:" + line, e);
                        }
                    }
                    // 解析完成，防止再有效期内重复解析
                    long end = System.currentTimeMillis();
                    long ttlInMill = revitConstructionTtl * 60 * 60 * 1000 - (end - begin);
                    redisTemplate.opsForValue().set(databagId, "parsed", ttlInMill, TimeUnit.MILLISECONDS);
                    logger.info("parsed {} revit constructions.", lineCount);
                } catch (IOException e) {
                    logger.error(e.getMessage(), e);
                } catch (ParserException e) {
                    logger.error(e.getMessage(), e);
                } finally {
                    if (reader != null) {
                        try {
                            reader.close();
                        } catch (IOException e) {
                        }
                    }
                    if (is != null) {
                        try {
                            is.close();
                        } catch (IOException e) {
                        }
                    }
                }
            }
        });
    }

    @Override
    public String getRevitConstruction(String databagId, String constructionId) {
        String construction = redisTemplate.opsForValue().get(databagId + ":" + constructionId);
        if (construction == null) {
            String parsing = redisTemplate.opsForValue().get(databagId);
            if (parsing != null) {// 初始化中
                throw PaasServerExceptionFactory
                        .notFoundException("data is loading, wait a while and try again.", null);
            } else {// 初始化已经完成，但没有找到构建，构建不存在
                throw PaasServerExceptionFactory.notFoundException("not found revit construction id:" + constructionId,
                        null);
            }
        }
        return construction;
    }

    /**
     * 解析构建Id.
     * 
     * @param databagId
     * @param line
     * @return
     * @throws Exception
     */
    private String parseConstructionId(String databagId, String line) throws Exception {
        String id = null;
        jsonij.json.Value idNode = JPath.evaluate(line, "/Id");
        if (idNode != null) {
            id = idNode.getString();
        }
        return id;
    }

    @PostConstruct
    public void init() {
        executor = Executors.newFixedThreadPool(revitParseThread);
    }
}
